package com.dtanner.books;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;

import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.tidy.Tidy;

/*
 *  Class that converts HTML to PDF using
 *  the DOM interfaces of JTidy, Xalan, and FOP.
 *
 *  @author N. Afshartous
 * 
 */
public class HTML2PDF implements ErrorListener{
	

	private final static Logger logger = Logger.getLogger(HTML2PDF.class);

	static private FopFactory fopFactory = FopFactory.newInstance();
	
	private Document doc;

//	public static void main(String[] args) {
//
//		// open file
//		if (args.length != 2) {
//			System.out.println("Usage: Html2Pdf htmlFile styleSheet");
//			System.exit(1);
//		}
//
//		FileInputStream input = null;
//		String htmlFileName = args[0];
//
//		try {
//
//			input = new FileInputStream(htmlFileName);
//
//		} catch (java.io.FileNotFoundException e) {
//			System.out.println("File not found: " + htmlFileName);
//		}
//
//		Tidy tidy = new Tidy();
//		Document xmlDoc = tidy.parseDOM(input, null);
//
//		Document foDoc = xml2FO(xmlDoc, args[1]);
//
//		String pdfFileName = htmlFileName.substring(0,
//				htmlFileName.indexOf("."))
//				+ ".pdf";
//		try {
//			OutputStream pdf = new FileOutputStream(new File(pdfFileName));
//			pdf.write(fo2PDF(foDoc));
//		} catch (java.io.FileNotFoundException e) {
//			System.out.println("Error creating PDF: " + pdfFileName);
//		} catch (java.io.IOException e) {
//			System.out.println("Error writing PDF: " + pdfFileName);
//		}
//
//	}
	
	public Document convertToXML(BufferedInputStream fis)
			throws FileNotFoundException {
		Tidy tidy = new Tidy();
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("temp.xml"));
		Document xmlDoc = tidy.parseDOM(fis, bos);
		try {
			bos.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		Document foDoc = xml2FO(xmlDoc, "xhtml2fo.xsl");
		setDoc(foDoc);
		return foDoc;
	}


	/*
	 * Applies stylesheet to input.
	 * 
	 * @param xml The xml input Document
	 * 
	 * @param stylesheet Name of the stylesheet
	 * 
	 * @return Document Result of the transform
	 */
	private Document xml2FO(Document xml, String styleSheet) {

		DOMSource xmlDomSource = new DOMSource(xml);
		DOMResult domResult = new DOMResult();

		Transformer transformer = getTransformer(styleSheet);
		
		if (transformer == null) {
			System.out.println("Error creating transformer for " + styleSheet);
			System.exit(1);
		}
		try {
			transformer.setErrorListener(this);
			transformer.transform(xmlDomSource, domResult);
			
		} catch (javax.xml.transform.TransformerException e) {
			return null;
		}
		logger.info(domResult.getNode());
		return (Document) domResult.getNode();

	}

	/*
	 * Apply FOP to XSL-FO input
	 * 
	 * @param foDocument The XSL-FO input
	 * 
	 * @return byte[] PDF result
	 */
	public static byte[] fo2PDF(Document foDocument) {
		ByteArrayOutputStream out = null;

		try {

			out = new ByteArrayOutputStream();

			Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);

			// Setup JAXP using identity transformer
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer(); // identity
																// transformer

			// Setup input stream
			Source src = new DOMSource(foDocument);

			// Resulting SAX events (the generated FO) must be piped through to
			// FOP
			Result res = new SAXResult(fop.getDefaultHandler());

			// Start XSLT transformation and FOP processing
			transformer.transform(src, res);
			out.flush();

			return out.toByteArray();

		} catch (Exception ex) {
			return null;
		} 
	}

	/*
	 * Create and return a Transformer for the specified stylesheet.
	 * 
	 * Based on the DOM2DOM.java example in the Xalan distribution.
	 */
	private static Transformer getTransformer(String styleSheet) {

		try {

			TransformerFactory tFactory = TransformerFactory.newInstance();

			DocumentBuilderFactory dFactory = DocumentBuilderFactory
					.newInstance();

			dFactory.setNamespaceAware(true);
			File file = new File(styleSheet);
			
			DocumentBuilder dBuilder = dFactory.newDocumentBuilder();
			Document xslDoc = dBuilder.parse(file);
			DOMSource xslDomSource = new DOMSource(xslDoc);

			return tFactory.newTransformer(xslDomSource);

		} catch (javax.xml.transform.TransformerException e) {
			e.printStackTrace();
			return null;
		} catch (java.io.IOException e) {
			e.printStackTrace();
			return null;
		} catch (javax.xml.parsers.ParserConfigurationException e) {
			e.printStackTrace();
			return null;
		} catch (org.xml.sax.SAXException e) {
			e.printStackTrace();
			return null;
		}

	}

	public Document getDoc() {
		return doc;
	}

	public void setDoc(Document doc) {
		this.doc = doc;
	}


	@Override
	public void warning(TransformerException exception)
			throws TransformerException {
		logger.warn(StackToString.toString(exception));
		
	}


	@Override
	public void error(TransformerException exception)
			throws TransformerException {
		logger.error(StackToString.toString(exception));
		
	}


	@Override
	public void fatalError(TransformerException exception)
			throws TransformerException {
		logger.fatal(StackToString.toString(exception));
		
	}

}
